package com.example.demo.aop;

/**
 *  AspectJ ⽀持三种通配符
 *  * ：匹配任意字符，只匹配⼀个元素（包，类，或⽅法，⽅法参数）
 * .. ：匹配任意字符，可以匹配多个元素 ，在表示类时，必须和 * 联合使⽤。
 *  + ：表示按照类型匹配指定类的所有类，必须跟在类名后⾯，如 com.cad.Car+ ,表示继承该类的所有⼦类包括本身
 * 切点表达式由切点函数组成，其中 execution() 是最常⽤的切点函数，⽤来匹配⽅法，语法为：
 *      execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
 *  修饰符和异常可以省略，具体含义见课件.
 */

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 *  2. 定义切面 (创建切面类)
 */
@Component
@Aspect // 表示当前类是一个切面类.
public class UserAspect {

    /**
     *  3. 定义切点(配置拦截规则), 这里使用 AspectJ 表达式语法.
     */
    // 第一个 * 表示拦截所有的返回值.
    // com.example.demo.aop.UserAspect 表示拦截的类(一般拦截的是controller包下的类).
    // 第二个 * 表示拦截类里的所有方法.
    // (..) 表示无论方法的参数是啥 都进行拦截.
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 该方法不需要有实现代码, 实现代码在 通知 里写. 因为它是[一对多]的.
    public void pointcut() { }

    /**
     *  4. 定义通知的实现.
     */
    // 4.1 前置通知
    @Before("pointcut()")
    // "pointcut()" (方法名)表示该通知是针对哪一个拦截规则.
    public void beforeAdvice() {
        System.out.println("执行了前置通知.");
    }
    // 4.2 后置通知
    @After("pointcut()")
    // "pointcut()" (方法名)表示该通知是针对哪一个拦截规则.
    public void afterAdvice() {
        System.out.println("执行了后置通知.");
    }
    // 4.3 环绕通知
    @Around("pointcut()")
    // 返回值必须是 Object.   参数类型必须是 ProceedingJoinPoint.
    public Object aroundActive(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = null;
        System.out.println("进入环绕通知.");
        obj = joinPoint.proceed();
        System.out.println("退出环绕通知.");
        return obj;
    }


}